#region Imports

using System;
using System.Drawing;
using System.Drawing.Design;
using System.Drawing.Text;
using System.Windows.Forms;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Runtime.InteropServices;

#endregion

namespace MaterialSkin.Controls
{

    #region MaterialListBox

    [DefaultProperty("Items")]
    [DefaultEvent("SelectedIndexChanged")]
    [ComVisible(true)]
    public class MaterialListBox : Control, IMaterialControl
    {
        #region Internal Vars

        private ObservableCollection<MaterialListBoxItem> _items = new ObservableCollection<MaterialListBoxItem>();
        //multi select
        private List<object> _selectedItems;
        private List<object> _indicates;
        private bool _multiSelect;

        private int _preSelectedIndex;
        //item index for single selection or first item index for multi selection
        //click event or key down/up will change _selectedIndex value
        private int _selectedIndex;
        private MaterialListBoxItem _selectedItem = null;
        private string _selectedText = null;
        private bool _showScrollBar;

        private bool _ctrlPressed = false;
        private bool _shiftPressed = false;
        private bool _multiKeyDown;
        private int _hoveredItem;
        private MaterialScrollBar _scrollBar;
        private object _selectedValue = null;

        private bool _updating = false;
        private int _itemHeight;

        private int itemCountInBox = 0;
        private bool _showBorder;
        private Color _borderColor;
        private Font _primaryFont;
        private Font _secondaryFont;

        private const int _leftrightPadding = 16;
        private int _primaryTextBottomPadding = 0;
        private int _secondaryTextTopPadding = 0;
        private int _secondaryTextBottomPadding = 0;

        public enum ListBoxStyle
        {
            SingleLine,
            TwoLine,
            ThreeLine
        }
        private ListBoxStyle _style = ListBoxStyle.SingleLine;

        public enum MaterialItemDensity
        {
            Default,
            Dense
        }

        private MaterialItemDensity _density;

        #endregion Internal Vars


        #region Properties

        //Properties for managing the material design properties
        [Browsable(false)]
        public int Depth { get; set; }

        [Browsable(false)]
        public MaterialSkinManager SkinManager => MaterialSkinManager.Instance;

        [Browsable(false)]
        public MouseState MouseState { get; set; }

        private bool useAccentColor;

        [Category("Material Skin"), DefaultValue(false), DisplayName("Use Accent Color")]
        public bool UseAccentColor
        {
            get { return useAccentColor; }
            set { useAccentColor = value; _scrollBar.UseAccentColor = value; Invalidate(); }
        }

        [TypeConverter(typeof(CollectionConverter))]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        [Editor(typeof(MaterialItemCollectionEditor), typeof(UITypeEditor))]
        [Category("Material Skin"), Description("Gets the items of the ListBox.")]
        public ObservableCollection<MaterialListBoxItem> Items => _items;

        [Browsable(false)]
        [Category("Material Skin"), Description("Gets a collection containing the currently selected items in the ListBox.")]
        public List<object> SelectedItems => _selectedItems;

        [Browsable(false), Category("Material Skin"), Description("Gets or sets the currently selected item in the ListBox.")]
        public MaterialListBoxItem SelectedItem
        {
            get => _selectedItem;
            set
            {
                _selectedItem = value;
                _selectedIndex = _items.IndexOf(_selectedItem);
                updateSelection();
                Invalidate();
            }
        }

        [Browsable(false), Category("Material Skin"),
         Description("Gets the currently selected Text in the ListBox.")]
        public string SelectedText
        {
            get => _selectedText;
            //set
            //{
            //    _selectedText = value;
            //    Invalidate();
            //}
        }

        [Browsable(false), Category("Material Skin"), Description("Gets or sets the zero-based index of the currently selected item in a ListBox.")]
        public int SelectedIndex
        {
            get => _selectedIndex;
            set
            {
                _selectedIndex = value;
                updateSelection();
                Invalidate();
            }
        }

        [Browsable(true), Category("Material Skin"), Description("Gets the value of the member property specified by the ValueMember property.")]
        public object SelectedValue
        {
            get => _selectedValue;
            //set
            //{
            //    _selectedValue = value;
            //    Invalidate();
            //}
        }

        [Category("Material Skin"), DefaultValue(false), Description("Gets or sets a value indicating whether the ListBox supports multiple rows.")]
        public bool MultiSelect
        {
            get => _multiSelect;
            set
            {
                _multiSelect = value;

                if (_selectedItems.Count > 1)
                {
                    _selectedItems.RemoveRange(1, _selectedItems.Count - 1);
                }

                Invalidate();
            }
        }

        [Browsable(false)]
        public int Count => _items.Count;

        [Category("Material Skin"), DefaultValue(false), Description("Gets or sets a value indicating whether the vertical scroll bar be shown or not.")]
        public bool ShowScrollBar
        {
            get => _showScrollBar;
            set
            {
                _showScrollBar = value;
                _scrollBar.Visible = value;
                Invalidate();
            }
        }

        [Category("Material Skin"), DefaultValue(true), Description("Gets or sets a value indicating whether the border shown or not.")]
        public bool ShowBorder
        {
            get => _showBorder;
            set
            {
                _showBorder = value;
                Refresh();
            }
        }

        [Category("Material Skin"), Description("Gets or sets backcolor used by the control.")]
        public override Color BackColor { get; set; }

        [Category("Material Skin"), Description("Gets or sets forecolor used by the control.")]
        public override Color ForeColor { get; set; }

        [Browsable(false)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public override string Text { get => base.Text; set => base.Text = value; }

        [Category("Material Skin"), Description("Gets or sets border color used by the control.")]
        public Color BorderColor
        {
            get => _borderColor;
            set
            {
                _borderColor = value;
                Refresh();
            }
        }

        [Category("Material Skin"), DefaultValue(ListBoxStyle.SingleLine)]
        [Description("Gets or sets the control style.")]
        public ListBoxStyle Style
        {
            get => _style;
            set
            {
                _style = value;
                UpdateItemSpecs();

                InvalidateScroll(this, null);
                Refresh();
            }
        }

        [Category("Material Skin"), DefaultValue(MaterialItemDensity.Dense)]
        [Description("Gets or sets list density")]
        public MaterialItemDensity Density
        {
            get { return _density; }
            set
            {
                _density = value;
                UpdateItemSpecs();
                Invalidate();
            }
        }

        #endregion Properties

        #region Constructors

        public MaterialListBox()
        {
            SetStyle
            (
                ControlStyles.UserPaint |
                ControlStyles.AllPaintingInWmPaint |
                ControlStyles.Selectable |
                ControlStyles.ResizeRedraw |
                ControlStyles.OptimizedDoubleBuffer |
                ControlStyles.SupportsTransparentBackColor,
                    true
            );
            UpdateStyles();
            base.BackColor = Color.Transparent;
            base.Font = SkinManager.getFontByType(MaterialSkinManager.fontType.Subtitle1);
            _secondaryFont = SkinManager.getFontByType(MaterialSkinManager.fontType.Body1);
            SetDefaults();
            ShowBorder = true;
            ShowScrollBar = false;
            MultiSelect = false;
            UseAccentColor = false;
            ForeColor = SkinManager.TextHighEmphasisColor; // Color.Black;
            BackColor = Color.White;
            BorderColor = Color.LightGray;
            UpdateProperties();
        }

        private void SetDefaults()
        {
            //SelectedIndex = -1;
            _selectedIndex = -1;
            _preSelectedIndex = -1;

            _hoveredItem = -1;
            _showScrollBar = false;
            _items.CollectionChanged += InvalidateScroll;
            _selectedItems = new List<object>();
            _indicates = new List<object>();
            _multiKeyDown = false;
            _scrollBar = new MaterialScrollBar()
            {
                Orientation = MaterialScrollOrientation.Vertical,
                Size = new Size(12, Height),
                Maximum = _items.Count * _itemHeight,
                SmallChange = _itemHeight,
                LargeChange = _itemHeight,

            };
            _scrollBar.Scroll += HandleScroll;
            //_scrollBar.MouseDown += VS_MouseDown;
            //_scrollBar.KeyDown += scrollBar_KeyDown;
            _scrollBar.BackColor = Color.Transparent;
     
            if (!Controls.Contains(_scrollBar))
            {
                Controls.Add(_scrollBar);
            }

            Style = ListBoxStyle.SingleLine;
            Density = MaterialItemDensity.Dense;
        }



        #endregion Constructors

        #region ApplyTheme


        private void UpdateProperties()
        {
            Invalidate();
        }

        private void UpdateItemSpecs()
        {
            if (_style == ListBoxStyle.TwoLine)
            {
                _secondaryTextTopPadding = 4;
                if (_density == MaterialItemDensity.Dense)
                {
                    _itemHeight = 60;
                    _secondaryTextBottomPadding = 10;
                    _primaryTextBottomPadding = 2;
                    _primaryFont = SkinManager.getFontByType(MaterialSkinManager.fontType.Body1);
                    _secondaryFont = SkinManager.getFontByType(MaterialSkinManager.fontType.Body2);
                }
                else
                {
                    _itemHeight = 72;
                    _secondaryTextBottomPadding = 16;
                    _primaryTextBottomPadding = 4;
                    _primaryFont = SkinManager.getFontByType(MaterialSkinManager.fontType.Subtitle1);
                    _secondaryFont = SkinManager.getFontByType(MaterialSkinManager.fontType.Body1);
                }
            }
            else if (_style == ListBoxStyle.ThreeLine)
            {
                _primaryTextBottomPadding = 4;
                _secondaryTextTopPadding = 4;
                if (_density == MaterialItemDensity.Dense)
                {
                    _itemHeight = 76;
                    _secondaryTextBottomPadding = 16;
                    _primaryFont = SkinManager.getFontByType(MaterialSkinManager.fontType.Body1);
                    _secondaryFont = SkinManager.getFontByType(MaterialSkinManager.fontType.Body2);
                }
                else
                {
                    _itemHeight = 88;
                    _secondaryTextBottomPadding = 12;
                    _primaryFont = SkinManager.getFontByType(MaterialSkinManager.fontType.Subtitle1);
                    _secondaryFont = SkinManager.getFontByType(MaterialSkinManager.fontType.Body1);
                }
            }
            else
            {
                //SingleLine
                if (_density == MaterialItemDensity.Dense)
                    _itemHeight = 40;
                else
                    _itemHeight = 48;
                _primaryFont = SkinManager.getFontByType(MaterialSkinManager.fontType.Subtitle1);
                _secondaryFont = SkinManager.getFontByType(MaterialSkinManager.fontType.Body1);
            }

        }

        private void SetFont()
        {
            if (_style == ListBoxStyle.TwoLine)
            {
                if (_density == MaterialItemDensity.Dense)
                {

                    _primaryFont = SkinManager.getFontByType(MaterialSkinManager.fontType.Body1);
                    _secondaryFont = SkinManager.getFontByType(MaterialSkinManager.fontType.Body2);
                }
                else
                {

                    _primaryFont = SkinManager.getFontByType(MaterialSkinManager.fontType.Subtitle1);
                    _secondaryFont = SkinManager.getFontByType(MaterialSkinManager.fontType.Body1);
                }
            }
            else if (_style == ListBoxStyle.ThreeLine)
            {
                if (_density == MaterialItemDensity.Dense)
                {
                    _primaryFont = SkinManager.getFontByType(MaterialSkinManager.fontType.Body1);
                    _secondaryFont = SkinManager.getFontByType(MaterialSkinManager.fontType.Body2);
                }
                else
                {
                    _primaryFont = SkinManager.getFontByType(MaterialSkinManager.fontType.Subtitle1);
                    _secondaryFont = SkinManager.getFontByType(MaterialSkinManager.fontType.Body1);
                }
            }
            else
            {
                //SingleLine
                _primaryFont = SkinManager.getFontByType(MaterialSkinManager.fontType.Subtitle1);
                _secondaryFont = SkinManager.getFontByType(MaterialSkinManager.fontType.Body1);
            }
        }

        #endregion ApplyTheme

        #region Draw Control

        protected override void OnPaint(PaintEventArgs e)
        {
            if (_updating == true) return;

            Graphics g = e.Graphics;
            g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
            Rectangle mainRect = new Rectangle(0, 0, Width - (ShowBorder ? 1 : 0), Height - (ShowBorder ? 1 : 0));

            int lastItem = lastIndexInBox() + 1;//(_scrollBar.Value / _itemHeight) + (Height / _itemHeight) + 1 > Items.Count ? Items.Count : (_scrollBar.Value / _itemHeight) + (Height / _itemHeight) + 1;
            int firstItem = firstIndexInBox();//_scrollBar.Value / _itemHeight < 0 ? 0 : (_scrollBar.Value / _itemHeight);


            g.FillRectangle(Enabled ? SkinManager.BackgroundBrush : SkinManager.BackgroundDisabledBrush, mainRect);

            //Set TextAlignFlags
            NativeTextRenderer.TextAlignFlags primaryTextAlignFlags;
            NativeTextRenderer.TextAlignFlags secondaryTextAlignFlags = NativeTextRenderer.TextAlignFlags.Left | NativeTextRenderer.TextAlignFlags.Top;
            if (_style == ListBoxStyle.TwoLine || _style == ListBoxStyle.ThreeLine)
            {
                primaryTextAlignFlags = NativeTextRenderer.TextAlignFlags.Left | NativeTextRenderer.TextAlignFlags.Bottom;
            }
            else
            {
                //SingleLine
                primaryTextAlignFlags = NativeTextRenderer.TextAlignFlags.Left | NativeTextRenderer.TextAlignFlags.Middle;
            }

            //Set color and brush
            Color selectedColor = new Color();
            if (UseAccentColor)
                selectedColor = SkinManager.ColorScheme.AccentColor;
            else
                selectedColor = SkinManager.ColorScheme.PrimaryColor;
            SolidBrush SelectedBrush = new SolidBrush(selectedColor);

            Color preSelectedColor = Color.FromArgb(40, selectedColor.R, selectedColor.G, selectedColor.B);
            SolidBrush preSelectedBrush = new SolidBrush(preSelectedColor);

            //Draw items
            for (int i = firstItem; i < lastItem; i++)
            {
                string itemText = Items[i].Text;
                string itemSecondaryText = Items[i].SecondaryText;

                Rectangle itemRect = new Rectangle(0, (i - firstItem) * _itemHeight, Width - (_showScrollBar && _scrollBar.Visible ? _scrollBar.Width : 0), _itemHeight);

                //currently multi selection operation now
                //if (_multiSelect && _indicates.Count != 0)
                if(_multiSelect && _multiKeyDown)
                {
                    if ((i == _selectedIndex || i ==_preSelectedIndex) && i != _hoveredItem && !_indicates.Contains(i))
                    {
                        g.FillRectangle(Enabled ?
                            preSelectedBrush :
                            new SolidBrush(DrawHelper.BlendColor(preSelectedColor, SkinManager.SwitchOffDisabledThumbColor, 197)),
                            itemRect);
                    }
                    else if (i == _hoveredItem && !_indicates.Contains(i))
                    {
                        g.FillRectangle(SkinManager.BackgroundHoverBrush, itemRect);
                    }
                    else if (_indicates.Contains(i))
                    {
                        g.FillRectangle(Enabled ?
                            SelectedBrush :
                            new SolidBrush(DrawHelper.BlendColor(selectedColor, SkinManager.SwitchOffDisabledThumbColor, 197)),
                            itemRect);
                    }
                }
                else//currently single selection operation now
                {
                    if (i == _preSelectedIndex && i != _selectedIndex && i != _hoveredItem)
                    {
                        g.FillRectangle(Enabled ?
                            preSelectedBrush :
                            new SolidBrush(DrawHelper.BlendColor(preSelectedColor, SkinManager.SwitchOffDisabledThumbColor, 197)),
                            itemRect);
                    }
                    else if (i == _hoveredItem && i != _selectedIndex)
                    {
                        g.FillRectangle(SkinManager.BackgroundHoverBrush, itemRect);
                    }

                    else if (i == _selectedIndex)
                    {
                        g.FillRectangle(Enabled ?
                            SelectedBrush :
                            new SolidBrush(DrawHelper.BlendColor(selectedColor, SkinManager.SwitchOffDisabledThumbColor, 197)),
                            itemRect);
                    }
                }

                //Define primary & secondary Text Rect
                Rectangle primaryTextRect = new Rectangle(itemRect.X + _leftrightPadding, itemRect.Y, itemRect.Width - (2 * _leftrightPadding), itemRect.Height);
                Rectangle secondaryTextRect = new Rectangle();

                if (_style == ListBoxStyle.TwoLine)
                {
                    primaryTextRect.Height = (primaryTextRect.Height / 2) - _primaryTextBottomPadding;
                }
                else if (_style == ListBoxStyle.ThreeLine)
                {
                    if (_density == MaterialItemDensity.Default)
                    {
                        primaryTextRect.Height = 36 - _primaryTextBottomPadding;
                    }
                    else
                    {
                        primaryTextRect.Height = 30 - _primaryTextBottomPadding;
                    }
                }
                secondaryTextRect = new Rectangle(primaryTextRect.X, primaryTextRect.Y + primaryTextRect.Height + (_primaryTextBottomPadding + _secondaryTextTopPadding), primaryTextRect.Width, _itemHeight - _secondaryTextBottomPadding - primaryTextRect.Height - (_primaryTextBottomPadding + _secondaryTextTopPadding));
                SetFont();
                using (NativeTextRenderer NativeText = new NativeTextRenderer(g))
                {
                    NativeText.DrawTransparentText(
                    itemText,
                    _primaryFont,
                    Enabled ? (i != _selectedIndex || (_multiSelect && _multiKeyDown && !_indicates.Contains(_selectedIndex)) || UseAccentColor) ?
                    SkinManager.TextHighEmphasisColor :
                    SkinManager.ColorScheme.TextColor :
                    SkinManager.TextDisabledOrHintColor, // Disabled
                    primaryTextRect.Location,
                    primaryTextRect.Size,
                    primaryTextAlignFlags);
                    if (_style == ListBoxStyle.TwoLine)
                    {
                        NativeText.DrawTransparentText(
                        itemSecondaryText,
                        _secondaryFont,
                        Enabled ? (i != SelectedIndex || UseAccentColor) ?
                        SkinManager.TextDisabledOrHintColor :
                        SkinManager.ColorScheme.TextColor.Darken(0.25f) :
                        SkinManager.TextDisabledOrHintColor, // Disabled
                        secondaryTextRect.Location,
                        secondaryTextRect.Size,
                        secondaryTextAlignFlags);
                    }
                    else if (_style == ListBoxStyle.ThreeLine)
                    {
                        NativeText.DrawMultilineTransparentText(
                        itemSecondaryText,
                        _secondaryFont,
                        Enabled ? (i != SelectedIndex || UseAccentColor) ?
                        SkinManager.TextDisabledOrHintColor :
                        SkinManager.ColorScheme.TextColor.Darken(0.25f) :
                        SkinManager.TextDisabledOrHintColor, // Disabled
                        secondaryTextRect.Location,
                        secondaryTextRect.Size,
                        secondaryTextAlignFlags);
                    }
                }

            }
            if (ShowBorder)
            {
                g.DrawRectangle(Pens.LightGray, mainRect);
            }
        }

        #endregion Draw Control

        #region Methods

        public void AddItem(MaterialListBoxItem newItem)
        {
            _items.Add(newItem);
            InvalidateScroll(this, null);
            ItemsCountChanged?.Invoke(this, new EventArgs());
        }

        public void AddItem(string newItem)
        {
            MaterialListBoxItem _newitemMLBI = new MaterialListBoxItem(newItem);
            _items.Add(_newitemMLBI);
            InvalidateScroll(this, null);
            ItemsCountChanged?.Invoke(this, new EventArgs());
        }

        public void AddItems(MaterialListBoxItem[] newItems)
        {
            _updating = true;
            foreach (MaterialListBoxItem str in newItems)
            {
                AddItem(str);
            }
            _updating = false;

            InvalidateScroll(this, null);
            ItemsCountChanged?.Invoke(this, new EventArgs());
        }

        public void AddItems(string[] newItems)
        {
            _updating = true;
            foreach (string str in newItems)
            {
                AddItem(str);
            }
            _updating = false;

            InvalidateScroll(this, null);
            ItemsCountChanged?.Invoke(this, new EventArgs());
        }

        public void RemoveItemAt(int index)
        {
            if (index <= _selectedIndex)
            {
                _selectedIndex -= 1;
                updateSelection();
            }
            _items.RemoveAt(index);
            InvalidateScroll(this, null);
            ItemsCountChanged?.Invoke(this, new EventArgs());
        }

        public void RemoveItem(MaterialListBoxItem item)
        {
            if (_items.IndexOf(item) <= _selectedIndex)
            {
                _selectedIndex -= 1;
                updateSelection();
            }
            _items.Remove(item);
            InvalidateScroll(this, null);
            ItemsCountChanged?.Invoke(this, new EventArgs());
        }

        public int IndexOf(MaterialListBoxItem value)
        {
            return _items.IndexOf(value);
        }

        public void RemoveItems(MaterialListBoxItem[] itemsToRemove)
        {
            _updating = true;
            foreach (MaterialListBoxItem item in itemsToRemove)
            {
                if (_items.IndexOf(item) <= _selectedIndex)
                {
                    _selectedIndex -= 1;
                    updateSelection();
                }
                _items.Remove(item);
            }
            _updating = false;

            InvalidateScroll(this, null);
            ItemsCountChanged?.Invoke(this, new EventArgs());
        }

        private int firstIndexInBox()
        {
            return _scrollBar.Value / _itemHeight < 0 ? 0 : (_scrollBar.Value / _itemHeight);
        }

        private int lastIndexInBox()
        {
            return (_scrollBar.Value / _itemHeight) + (Height / _itemHeight) >= Items.Count ? Items.Count - 1 : (_scrollBar.Value / _itemHeight) + (Height / _itemHeight);
        }
        private void updateSelection()
        {
            if (_selectedIndex >= 0)
            {
                _selectedItem = _items[_selectedIndex];
                _selectedValue = _items[_selectedIndex];
                _selectedText = _items[_selectedIndex].ToString();
            }
            else
            {
                _selectedItem = null;
                _selectedValue = null;
                _selectedText = null;
            }
        }

        public void Clear()
        {
            _updating = true;
            for (int i = _items.Count - 1; i >= 0; i += -1)
            {
                _items.RemoveAt(i);
            }
            _updating = false;
            _selectedIndex = -1;
            updateSelection();

            InvalidateScroll(this, null);
            ItemsCountChanged?.Invoke(this, new EventArgs());
        }

        public void BeginUpdate()
        {
            _updating = true;
        }

        public void EndUpdate()
        {
            _updating = false;
        }

        #endregion Methods

        #region Events

        [Category("Behavior")]
        [Description("Occurs when selected index change.")]
        public event SelectedIndexChangedEventHandler SelectedIndexChanged;

        public delegate void SelectedIndexChangedEventHandler(object sender, MaterialListBoxItem selectedItem);

        [Category("Behavior")]
        [Description("Occurs when selected value change.")]
        public event SelectedValueEventHandler SelectedValueChanged;

        public delegate void SelectedValueEventHandler(object sender, MaterialListBoxItem selectedItem);

        [Category("Behavior")]
        [Description("Occurs when item is added or removed.")]
        public event EventHandler ItemsCountChanged;

        #endregion Events

        protected override void OnSizeChanged(EventArgs e)
        {
            InvalidateScroll(this, e);
            InvalidateLayout();

            itemCountInBox = Height / _itemHeight + 1;

            base.OnSizeChanged(e);
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            Focus();
            if (e.Button == MouseButtons.Left)
            {
                int index = _scrollBar.Value / _itemHeight + e.Location.Y / _itemHeight;
                if (index >= 0 && index < _items.Count)
                {
                    //if (_multiSelect && _multiKeyDown)
                    if(_multiSelect && _ctrlPressed)
                    {
                        _multiKeyDown = true;

                        //the running code transfer from single selection to multi selection,
                        //init one time
                        if (_multiKeySelectNeedInit)
                        {
                            _multiKeySelectNeedInit = false;
                            if (_selectedIndex >= 0 && !_indicates.Contains(_selectedIndex))
                            {
                                _indicates.Add(_selectedIndex);
                                _selectedItems.Add(_items[_selectedIndex]);
                            }
                        }
                        //
                        if(!_indicates.Contains(index))
                        {
                            _indicates.Add(index);
                            _selectedItems.Add(_items[index]);
                        }
                        else
                        {
                            _indicates.Remove(index);
                            _selectedItems.Remove(_items[index]);
                        }
                        _selectedIndex = index;
                        _preSelectedIndex = _selectedIndex;
                    }
                    else if(_multiSelect && _shiftPressed)
                    {
                        _multiKeyDown = true;
                        //not selected yet
                        if (_selectedIndex <= 0)
                        {
                            _indicates.Clear();
                            _selectedItems.Clear();
                            _selectedIndex = 0;
                            _preSelectedIndex = index;
                            for (int i = _selectedIndex; i <= _preSelectedIndex; ++i )
                            {
                                _indicates.Add(i);
                                _selectedItems.Add(_items[i]);
                            }
                            
                        }
                        else //after single select(ctrl key pressed or not)
                        {
                            _indicates.Clear();
                            _selectedItems.Clear();
                            _preSelectedIndex = index;
                            if (_selectedIndex < _preSelectedIndex)
                            {
                                for (int i = _selectedIndex; i <= _preSelectedIndex; ++i)
                                {
                                    _indicates.Add(i);
                                    _selectedItems.Add(_items[i]);
                                }
                            }
                            else
                            {
                                for (int i = _selectedIndex; i >= _preSelectedIndex; --i)
                                {
                                    _indicates.Add(i);
                                    _selectedItems.Add(_items[i]);
                                }
                            }

                        }
                    }
                    else
                    {
                        if (_ctrlPressed || _shiftPressed)
                            _multiKeyDown = true;
                        else
                            _multiKeyDown = false;

                        _indicates.Clear();
                        _selectedItems.Clear();
                        if (_ctrlPressed)
                        {
                            //item has been selected and currently item clicked is _selectedIndex item
                            if (_selectedIndex >= 0 && index == _selectedIndex)
                            {
                                _preSelectedIndex = _selectedIndex;
                                _selectedIndex = -1;
                                updateSelection();
                            }
                            else
                            {
                                _preSelectedIndex = -1;
                                _multiKeySelectNeedInit = true;
                                _selectedItem = _items[index];
                                _selectedIndex = index;
                                _selectedValue = _items[index];
                                _selectedText = _items[index].ToString();
                                SelectedIndexChanged?.Invoke(this, _selectedItem);
                                SelectedValueChanged?.Invoke(this, _selectedItem);
                            }
                        }
                        else
                        {
                            _preSelectedIndex = -1;
                            _multiKeySelectNeedInit = true;
                            _selectedItem = _items[index];
                            _selectedIndex = index;
                            _selectedValue = _items[index];
                            _selectedText = _items[index].ToString();
                            SelectedIndexChanged?.Invoke(this, _selectedItem);
                            SelectedValueChanged?.Invoke(this, _selectedItem);
                        }

                    }
                }
                Invalidate();
            }
            base.OnMouseDown(e);
        }

        private void HandleScroll(object sender, ScrollEventArgs e)
        {
            //it means the space between current value and max value of scrollbar can't contain the items
            //which total height is same as the control Height,
            //the change in the scrollbar control, so the following code line  is remarked
            //if (_scrollBar.Maximum - _scrollBar.Value < Height) _scrollBar.Value = _scrollBar.Maximum - Height;

            Invalidate();
        }

        private void InvalidateScroll(object sender, EventArgs e)
        {
            _scrollBar.Maximum = _items.Count * _itemHeight;
            _scrollBar.SmallChange = _itemHeight;
            _scrollBar.LargeChange = Height;
            _scrollBar.Visible = (_items.Count * _itemHeight) > Height;
            if (_items.Count == 0)
            { _scrollBar.Value = 0; }
            Invalidate();
        }

        //private void VS_MouseDown(object sender, MouseEventArgs e)
        //{
        //    //it seems it has effect but no use, because it focused by scrollbar itself
        //    Focus();
        //}
        //private void scrollBar_KeyDown(object sender, KeyEventArgs e)
        //{
        //    //focus is transfered from scroll bar to list box,
        //    //then process the list box key down event logic
        //    Console.WriteLine("123=" + e.KeyCode);
        //    Focus();
        //    if (e.KeyCode == Keys.Down)
        //    {
        //        _multiKeySelectNeedInit = true;
        //        //clear multi select
        //        if (_indicates.Count > 0)
        //        {
        //            _indicates.Clear();
        //            _selectedItems.Clear();
        //        }
        //        //single select processing
        //        int lastItem = lastIndexInBox();

        //        if (_preSelectedIndex >= 0)
        //        {
        //            _selectedIndex = _preSelectedIndex;
        //            _preSelectedIndex = -1;
        //        }
        //        _selectedIndex += 1;
        //        if (_selectedIndex >= _items.Count)
        //        {
        //            _selectedIndex = _items.Count - 1;
        //        }
        //        else if (_selectedIndex > lastItem)
        //        {
        //            _scrollBar.Value += _scrollBar.SmallChange;
        //        }

        //        updateSelection();

        //        Invalidate();

        //    }
        //    else if(e.KeyCode == Keys.Up)
        //    {
        //        _multiKeySelectNeedInit = true;
        //        //if (_selectedIndex < 0) return;
        //        //
        //        //clear multi select
        //        if (_indicates.Count > 0)
        //        {
        //            _indicates.Clear();
        //            _selectedItems.Clear();
        //        }
        //        //single select processing
        //        int firstItem = firstIndexInBox();
        //        if (_preSelectedIndex >= 0)
        //        {
        //            _selectedIndex = _preSelectedIndex;
        //            _preSelectedIndex = -1;
        //        }

        //        _selectedIndex -= 1;
        //        if (_selectedIndex < 0)
        //        {
        //            _selectedIndex = 0;
        //        }
        //        else if (_selectedIndex < firstItem)
        //        {
        //            _scrollBar.Value -= _scrollBar.SmallChange;
        //        }
        //        updateSelection();
        //        Invalidate();
        //    }


        //}
        private void InvalidateLayout()
        {
            _scrollBar.Size = new Size(12, Height - (ShowBorder ? 2 : 0));
            _scrollBar.Location = new Point(Width - (_scrollBar.Width + (ShowBorder ? 1 : 0)), ShowBorder ? 1 : 0);
            Invalidate();
        }

        private void processKeyDownMultiSelect(int _selectedIndex, int _preSelectedIndex)
        {
            if (_preSelectedIndex < 0) return;

            if (_preSelectedIndex > _selectedIndex && !_indicates.Contains(_preSelectedIndex))
            {
                _indicates.Add(_preSelectedIndex);
                _selectedItems.Add(_items[_preSelectedIndex]);
            }
            else if (_selectedIndex > _preSelectedIndex)
            {
                _indicates.Remove(_preSelectedIndex);
                _selectedItems.Remove(_items[_preSelectedIndex]);
            }
        }

        private void processKeyUpMultiSelect(int _selectedIndex, int _preSelectedIndex)
        {

            //Console.WriteLine("_preSelectedIndex=" + _preSelectedIndex + ", firstItem=" + firstItem);
            if (_preSelectedIndex < 0) return;

            if (_preSelectedIndex > _selectedIndex)
            {
                _indicates.Remove(_preSelectedIndex);
                _selectedItems.Remove(_items[_preSelectedIndex]);
            }
            else if (_selectedIndex > _preSelectedIndex && !_indicates.Contains(_preSelectedIndex))
            {
                _indicates.Add(_preSelectedIndex);
                _selectedItems.Add(_items[_preSelectedIndex]);
            }
        }

        private void processKeyPageDownUpHomeEndMultiSelect(int _startIndex, int _selectedIndex, int _preSelectedIndex)
        {
            if (_startIndex < 0 || _preSelectedIndex < 0) return;


            if(_startIndex > _selectedIndex && _preSelectedIndex >= _startIndex)
            {
                for (int i = _startIndex + 1; i <= _preSelectedIndex; ++i)
                {
                    _indicates.Add(i);
                    _selectedItems.Add(_items[i]);
                }

            }
            else if (_startIndex > _selectedIndex && _preSelectedIndex < _startIndex && _preSelectedIndex > _selectedIndex)
            {
                for (int i = _startIndex; i > _preSelectedIndex; --i)
                {
                    _indicates.Remove(i);
                    _selectedItems.Remove(_items[i]);
                }
            }
            else if(_startIndex > _selectedIndex && _preSelectedIndex <= _selectedIndex)
            {
                for (int i = _startIndex; i >= _selectedIndex + 1; --i)
                {
                    _indicates.Remove(i);
                    _selectedItems.Remove(_items[i]);
                }
                for (int i = _selectedIndex - 1; i >= _preSelectedIndex; --i)
                {
                    _indicates.Add(i);
                    _selectedItems.Add(_items[i]);
                }
            }
            else if(_startIndex <= _selectedIndex && _preSelectedIndex <= _startIndex)
            {
                for (int i = _startIndex - 1; i >= _preSelectedIndex && i != _selectedIndex; --i)
                {
                    _indicates.Add(i);
                    _selectedItems.Add(_items[i]);
                }
            }
            else if (_startIndex <= _selectedIndex && _preSelectedIndex > _startIndex && _preSelectedIndex <= _selectedIndex)
            {
                for (int i = _startIndex; i < _preSelectedIndex && i  != _selectedIndex; ++i)
                {
                    _indicates.Remove(i);
                    _selectedItems.Remove(_items[i]);
                }
            }
            else if (_startIndex <= _selectedIndex && _preSelectedIndex > _selectedIndex)
            {
                for (int i = _startIndex; i <= _selectedIndex - 1; ++i)
                {
                    _indicates.Remove(i);
                    _selectedItems.Remove(_items[i]);
                }
                for (int i = _selectedIndex + 1; i <= _preSelectedIndex; ++i)
                {
                    _indicates.Add(i);
                    _selectedItems.Add(_items[i]);
                }
            }


        }

        private void resetAddMultiSelect(int _selectedIndex, int _preSelectedIndex)
        {
            //if (_selectedIndex >= 0 && _preSelectedIndex >= 0)
           // {
                //isMinusFlag = false;
                if (_indicates.Count > 0 && _selectedIndex == Convert.ToInt32(_indicates[0]) &&
                    _preSelectedIndex == Convert.ToInt32(_indicates[_indicates.Count - 1]) &&
                    _indicates.Count == Math.Abs(_selectedIndex - _preSelectedIndex) + 1)
                {
                    //do nothing
                }
                else
                {
                    _indicates.Clear();
                    _selectedItems.Clear();
                    if (_preSelectedIndex >= _selectedIndex)
                    {
                        for (int i = _selectedIndex; i <= _preSelectedIndex; ++i)
                        {
                            _indicates.Add(i);
                            _selectedItems.Add(_items[i]);
                        }
                    }
                    else
                    {
                        for (int i = _selectedIndex; i >= _preSelectedIndex; --i)
                        {
                            _indicates.Add(i);
                            _selectedItems.Add(_items[i]);
                        }
                    }
                }
            //}
        }

        protected override void OnMouseWheel(MouseEventArgs e)
        {
            if (_scrollBar.Visible == true)
            {
                if (_scrollBar.Minimum > _scrollBar.Value - e.Delta / 2)
                    _scrollBar.Value = _scrollBar.Minimum;
                else if (_scrollBar.Maximum < _scrollBar.Value + Height)
                {
                    if (e.Delta > 0)
                        _scrollBar.Value -= e.Delta / 2;
                    else
                    { } //Do nothing, maximum reached
                }
                else
                    _scrollBar.Value -= e.Delta / 2;

                updateHoveredItem(e);

                Invalidate();
                base.OnMouseWheel(e);
                ((HandledMouseEventArgs)e).Handled = true;
            }
        }

        protected override void OnPreviewKeyDown(PreviewKeyDownEventArgs e)
        {

            if (e.KeyCode == Keys.Down || e.KeyCode == Keys.Up || 
                e.KeyCode == Keys.PageDown || e.KeyCode == Keys.PageUp || 
                e.KeyCode == Keys.Home || e.KeyCode == Keys.End) 
                e.IsInputKey = true;

            base.OnPreviewKeyDown(e);

        }

        //protected override bool IsInputKey(Keys keyData)
        //{

        //    switch (keyData)
        //    {
        //        case Keys.Down:
        //            try
        //            {
        //                ////_selectedItems.Remove(_items[SelectedIndex]);
        //                ////SelectedIndex += 1;
        //                ////_selectedItems.Add(_items[SelectedIndex]);
        //                //int lastItem = lastIndexInBox();
        //                //_selectedIndex += 1;
        //                //if(_selectedIndex >= _items.Count)
        //                //{
        //                //    _selectedIndex = _items.Count - 1;
        //                //}
        //                //else if (_selectedIndex > lastItem)
        //                //{
        //                //    _scrollBar.Value += _scrollBar.SmallChange;
        //                //}

        //                //updateSelection();
        //                //Invalidate();
        //                return true;
        //            }
        //            catch
        //            {
        //                //
        //            }
        //            break;

        //        case Keys.Up:
        //            try
        //            {
        //                ////_selectedItems.Remove(_items[SelectedIndex]);
        //                ////SelectedIndex -= 1;
        //                ////_selectedItems.Add(_items[SelectedIndex]);
        //                //int firstItem = firstIndexInBox();
        //                //_selectedIndex -= 1;
        //                //if(_selectedIndex < 0)
        //                //{
        //                //    _selectedIndex = 0;
        //                //}
        //                //else if(_selectedIndex < firstItem)
        //                //{
        //                //    _scrollBar.Value -= _scrollBar.SmallChange;
        //                //}
        //                //updateSelection();
        //                //Invalidate();
        //                return true;
        //            }
        //            catch
        //            {
        //                //
        //            }
        //            break;
        //        //case Keys.Shift:
        //        //case Keys.ShiftKey:
        //        //case Keys.LShiftKey:
        //        //case Keys.RShiftKey:
        //        //    return true;
        //        //    break;
        //    }

        //    return base.IsInputKey(keyData);
        //}

        private bool _multiKeySelectNeedInit = true;
        private bool isMinusFlag = false;
        protected override void OnKeyDown(KeyEventArgs e)
        {

            //pre single select whatever if the running code comes from the single selection or multi selection
            if (e.Control && !e.Shift)
            {
                _ctrlPressed = true;
                _shiftPressed = false;
                if (e.KeyCode == Keys.Down)
                {
                    _multiKeySelectNeedInit = true;
                    int lastItem = lastIndexInBox();
                    if (_preSelectedIndex < 0)
                        _preSelectedIndex = _selectedIndex;

                    _preSelectedIndex++;
                    if (_preSelectedIndex >= _items.Count)
                    {
                        _preSelectedIndex = _items.Count - 1;
                    }
                    if (_preSelectedIndex > lastItem)
                    {
                        _scrollBar.Value += _scrollBar.SmallChange;
                        if(_preSelectedIndex == _items.Count - 1)
                        {
                            _scrollBar.Value = _scrollBar.Maximum;
                        }
                    }

                    Invalidate();

                }
                else if (e.KeyCode == Keys.Up)
                {
                    
                    _multiKeySelectNeedInit = true;
                    if (_selectedIndex < 0 && _preSelectedIndex < 0) return;
                    int firstItem = firstIndexInBox();

                    if (_preSelectedIndex < 0)
                        _preSelectedIndex = _selectedIndex;

                    _preSelectedIndex--;
                    if (_preSelectedIndex < 0)
                    {
                        _preSelectedIndex = 0;
                    }
                    if (_preSelectedIndex < firstItem)
                    {
                        _scrollBar.Value -= _scrollBar.SmallChange;
                        if (_preSelectedIndex == 0)
                        {
                            _scrollBar.Value = _scrollBar.Minimum;
                        }
                    }
     
                    Invalidate();
                }
                else if(e.KeyCode == Keys.PageDown)
                {
                    _multiKeySelectNeedInit = true;
         
                    if (_preSelectedIndex < 0)
                        _preSelectedIndex = _selectedIndex;

                    int lastItem = lastIndexInBox();
                    if(_preSelectedIndex == lastItem)
                    {
                        _scrollBar.Value += _scrollBar.LargeChange;
                        _preSelectedIndex = lastIndexInBox();
                    }
                    else if(_preSelectedIndex < lastItem)
                    {
                        _preSelectedIndex = lastItem;
                    }

                    if (_preSelectedIndex == _items.Count - 1)
                    {
                        _scrollBar.Value = _scrollBar.Maximum;
                    }

                    Invalidate();
                }
                else if(e.KeyCode == Keys.PageUp)
                {
                    _multiKeySelectNeedInit = true;
                    if (_selectedIndex < 0 && _preSelectedIndex < 0) return;

                    if (_preSelectedIndex < 0)
                        _preSelectedIndex = _selectedIndex;

                    int firstItem = firstIndexInBox();
                    if(_preSelectedIndex == firstItem)
                    {
                        _scrollBar.Value -= _scrollBar.LargeChange;
                        _preSelectedIndex = firstIndexInBox();
                    }
                    else if(_preSelectedIndex > firstItem)
                    {
                        _preSelectedIndex = firstItem;
                    }

                    if (_preSelectedIndex == 0)
                    {
                        _scrollBar.Value = _scrollBar.Minimum;
                    }

                    Invalidate();
                }
                else if(e.KeyCode == Keys.Home)
                {
                    _multiKeySelectNeedInit = true;

                    _preSelectedIndex = 0;
                    _scrollBar.Value = _scrollBar.Minimum;

                    Invalidate();
                }
                else if(e.KeyCode == Keys.End)
                {
                    _multiKeySelectNeedInit = true;

                    _preSelectedIndex = _items.Count - 1;

                    _scrollBar.Value = _scrollBar.Maximum;

                    Invalidate();
                }

            }
            //multi select
            else if (!e.Control && e.Shift)
            {
                _ctrlPressed = false;
                _shiftPressed = true;
                if ((e.KeyCode == Keys.Down || e.KeyCode == Keys.PageDown || e.KeyCode == Keys.End) && _multiSelect)
                {
                    _multiKeyDown = true;
                    if (_multiKeySelectNeedInit)
                    {
                        _multiKeySelectNeedInit = false;
                        //just start multi selection operating now
                        if (_selectedIndex < 0)
                        {
                            isMinusFlag = true;
                            _selectedIndex = 0;
                            _preSelectedIndex = _selectedIndex;
                            if (!_indicates.Contains(_selectedIndex))
                            {
                                _indicates.Add(_selectedIndex);
                                _selectedItems.Add(_items[_selectedIndex]);
                                
                            }

                        }
                        //come from single selection to multi selection
                        else if (_selectedIndex >= 0 && _preSelectedIndex < 0)
                        {
                            isMinusFlag = false;
                            _preSelectedIndex = _selectedIndex;
                            if (!_indicates.Contains(_selectedIndex))
                            {
                                _indicates.Add(_selectedIndex);
                                _selectedItems.Add(_items[_selectedIndex]);
                            }
     
                        }
                        //come from ctrl+ down/up to multi selection
                        else if (_selectedIndex >= 0 && _preSelectedIndex >= 0)
                        {
                            isMinusFlag = false;
                            resetAddMultiSelect(_selectedIndex, _preSelectedIndex);
                        }
                        
                    }
                    else
                    {
                        if (_selectedIndex >= 0 && _preSelectedIndex >= 0)
                        {
                            resetAddMultiSelect(_selectedIndex, _preSelectedIndex);
                        }
                        isMinusFlag = false;
                    }
                    
                    if(!isMinusFlag)
                    {
                        if (e.KeyCode == Keys.Down)
                        {
                            if (_preSelectedIndex >= _selectedIndex)
                            {
                                _preSelectedIndex++;
                                int lastItem = lastIndexInBox();
                                if (_preSelectedIndex >= _items.Count)
                                    _preSelectedIndex = _items.Count - 1;
                                if (_preSelectedIndex > lastItem)
                                {
                                    _scrollBar.Value += _scrollBar.SmallChange;
                                    if (_preSelectedIndex == _items.Count - 1)
                                    {
                                        _scrollBar.Value = _scrollBar.Maximum;
                                    }
                                }
                                processKeyDownMultiSelect(_selectedIndex, _preSelectedIndex);
                            }
                            else
                            {
                                processKeyDownMultiSelect(_selectedIndex, _preSelectedIndex);
                                _preSelectedIndex++;
                                int lastItem = lastIndexInBox();
                                if (_preSelectedIndex >= _items.Count)
                                    _preSelectedIndex = _items.Count - 1;
                                if (_preSelectedIndex > lastItem)
                                {
                                    _scrollBar.Value += _scrollBar.SmallChange;
                                    if (_preSelectedIndex == _items.Count - 1)
                                    {
                                        _scrollBar.Value = _scrollBar.Maximum;
                                    }
                                }
                            }
                        }
                        else if (e.KeyCode == Keys.PageDown)
                        {
  
                            int startIndex = _selectedIndex;
                            if (_indicates.Count > 0 && _indicates.Count <= _items.Count)
                                startIndex = Convert.ToInt32(_indicates[_indicates.Count - 1]);
                            else if (_indicates.Count == _items.Count)
                                startIndex = -1;
                            
                            if(_preSelectedIndex >= _selectedIndex)
                            {
                                int lastItem = lastIndexInBox();
                                if (_preSelectedIndex == lastItem)
                                {
                                    _scrollBar.Value += _scrollBar.LargeChange;
                                    _preSelectedIndex = lastIndexInBox();

                                }
                                else if (_preSelectedIndex < lastItem)
                                {
                                    _preSelectedIndex = lastIndexInBox();
                                }

                                if (_preSelectedIndex == _items.Count - 1)
                                {
                                    _scrollBar.Value = _scrollBar.Maximum;
                                }
                            }
                            else
                            {

                                //_preSelectedIndex and _selectedIndex are in the same page
                                if ((_selectedIndex - _preSelectedIndex + 1) <= itemCountInBox)
                                {
                                    int firstItem = firstIndexInBox();
                                    //firstItem and _selectedIndex are not in the same page
                                    if ((_selectedIndex - firstItem + 1)  > itemCountInBox)
                                    {
                                        //_preSelectedIndex is consider as firstItem
                                        _preSelectedIndex = _selectedIndex + (itemCountInBox - (_selectedIndex - _preSelectedIndex + 1));
                                    }
                                    else
                                        _preSelectedIndex = lastIndexInBox();
                                    //
                                    if (_preSelectedIndex == _items.Count - 1)
                                    {
                                        _scrollBar.Value = _scrollBar.Maximum;
                                    }
                                }
                                else
                                {
                                    _scrollBar.Value += _scrollBar.LargeChange;
                                    _preSelectedIndex = firstIndexInBox();
                                }

                            }

                            processKeyPageDownUpHomeEndMultiSelect(startIndex, _selectedIndex, _preSelectedIndex);
                            //if (_preSelectedIndex >= _selectedIndex)
                            //{
                            //    _preSelectedIndex += _scrollBar.LargeChange / _itemHeight + 1;
                            //    if (_preSelectedIndex >= _items.Count)
                            //        _preSelectedIndex = _items.Count - 1;
                            //    _scrollBar.Value += _scrollBar.LargeChange;
                            //    if (_preSelectedIndex == _items.Count - 1)
                            //    {
                            //        _scrollBar.Value = _scrollBar.Maximum;
                            //    }
                            //    int startIndex = _selectedIndex;
                            //    if (_indicates.Count > 0 && _indicates.Count < _items.Count)
                            //        startIndex = Convert.ToInt32(_indicates[_indicates.Count - 1]) + 1;
                            //    else if (_indicates.Count == _items.Count)
                            //        startIndex = -1;

                            //    processKeyPageDownUpHomeEndMultiSelect(startIndex, _selectedIndex, _preSelectedIndex);

                            //}
                            //else
                            //{

                            //    processKeyDownMultiSelect(_selectedIndex, _preSelectedIndex);
                            //    _preSelectedIndex += _scrollBar.LargeChange / _itemHeight + 1;

                            //    if (_preSelectedIndex >= _items.Count)
                            //        _preSelectedIndex = _items.Count - 1;

                            //    _scrollBar.Value += _scrollBar.LargeChange;
                            //    if (_preSelectedIndex == _items.Count - 1)
                            //    {
                            //        _scrollBar.Value = _scrollBar.Maximum;
                            //    }
                            //}
                        }
                        else if (e.KeyCode == Keys.End)
                        {
                            _preSelectedIndex = _items.Count - 1;
                            _scrollBar.Value = _scrollBar.Maximum;

                            int startIndex = _selectedIndex;
                            
                            if (_indicates.Count > 0 && _indicates.Count <= _items.Count)
                                startIndex = Convert.ToInt32(_indicates[_indicates.Count - 1]);
                            else if (_indicates.Count == _items.Count)
                                startIndex = -1;
                            processKeyPageDownUpHomeEndMultiSelect(startIndex, _selectedIndex, _preSelectedIndex);

                            //if (_preSelectedIndex >= _selectedIndex)
                            //{
                            //    _preSelectedIndex = _items.Count - 1;
                            //    _scrollBar.Value = _scrollBar.Maximum;

                            //    processKeyDownMultiSelect(_selectedIndex, _preSelectedIndex);
                            //}
                            //else
                            //{
                            //    processKeyDownMultiSelect(_selectedIndex, _preSelectedIndex);
                            //    _preSelectedIndex = _items.Count - 1;
                            //    _scrollBar.Value = _scrollBar.Maximum;
                            //}
                        }

                    }
                    Invalidate();
                }
                else if ((e.KeyCode == Keys.Up || e.KeyCode == Keys.PageUp || e.KeyCode == Keys.Home) && _multiSelect)
                {
                    _multiKeyDown = true;
                    if (_selectedIndex < 0 && _preSelectedIndex < 0) return;


                    if (_multiKeySelectNeedInit)
                    {
                        _multiKeySelectNeedInit = false;
                        //come from single selection to multi selection
                        if (_selectedIndex >= 0 && _preSelectedIndex < 0)
                        {
                            _preSelectedIndex = _selectedIndex;
                            if (!_indicates.Contains(_selectedIndex))
                            {
                                _indicates.Add(_selectedIndex);
                                _selectedItems.Add(_items[_selectedIndex]);
                                
                            }
                        }
                        //come from ctrl+ down/up to multi selection
                        else if (_selectedIndex >= 0 && _preSelectedIndex >= 0)
                        {
                            resetAddMultiSelect(_selectedIndex, _preSelectedIndex);
                        }
                    }

                    //
                    if(e.KeyCode == Keys.Up)
                    {
                        if (_preSelectedIndex <= _selectedIndex)
                        {
                            //first minus, then process
                            _preSelectedIndex--;
                            int firstItem = firstIndexInBox();
                            if (_preSelectedIndex < 0) _preSelectedIndex = 0;
                            if (_preSelectedIndex < firstItem)
                            {
                                _scrollBar.Value -= _scrollBar.SmallChange;
                                if (_preSelectedIndex == 0)
                                {
                                    _scrollBar.Value = _scrollBar.Minimum;
                                }
                            }
                            processKeyUpMultiSelect(_selectedIndex, _preSelectedIndex);

                        }
                        else
                        {
                            //first process, then minus
                            processKeyUpMultiSelect(_selectedIndex, _preSelectedIndex);
                            _preSelectedIndex--;
                            int firstItem = firstIndexInBox();
                            if (_preSelectedIndex < 0) _preSelectedIndex = 0;
                            if (_preSelectedIndex < firstItem)
                            {
                                _scrollBar.Value -= _scrollBar.SmallChange;
                                if (_preSelectedIndex == 0)
                                {
                                    _scrollBar.Value = _scrollBar.Minimum;
                                }
                            }

                        }
                    }
                    else if(e.KeyCode == Keys.PageUp)
                    {

                        int startIndex = _selectedIndex;
                        if (_indicates.Count > 0 && _indicates.Count <= _items.Count)
                            startIndex = Convert.ToInt32(_indicates[_indicates.Count - 1]);
                        else if (_indicates.Count == 0)
                            startIndex = -1;

                        if(_preSelectedIndex <= _selectedIndex)
                        {
                            int firstItem = firstIndexInBox();
                            if (_preSelectedIndex == firstItem)
                            {
                                _scrollBar.Value -= _scrollBar.LargeChange;
                                _preSelectedIndex = firstIndexInBox();
                            }
                            else if (_preSelectedIndex > firstItem)
                            {
                                _preSelectedIndex = firstItem;
                            }

                            if (_preSelectedIndex == 0)
                            {
                                _scrollBar.Value = _scrollBar.Minimum;
                            }
                        }
                        else
                        {
                            //_preSelectedIndex and _selectedIndex are in the same page
                            if ((_preSelectedIndex - _selectedIndex + 1) <= itemCountInBox)
                            {
                                int lastItem = lastIndexInBox();
                                //lastItem and _selectedIndex are not in the same page
                                if ((lastItem - _selectedIndex + 1) > itemCountInBox)
                                {
                                    //_preSelectedIndex is consider as lastItem
                                    _preSelectedIndex = _selectedIndex - (itemCountInBox - (_preSelectedIndex - _selectedIndex + 1));
                                }
                                else
                                    _preSelectedIndex = firstIndexInBox();
                                //
                                if (_preSelectedIndex == 0)
                                {
                                    _scrollBar.Value = _scrollBar.Minimum;
                                }
                            }
                            else
                            {
                                _scrollBar.Value -= _scrollBar.LargeChange;
                                _preSelectedIndex = lastIndexInBox();
                            }

                        }


                        processKeyPageDownUpHomeEndMultiSelect(startIndex, _selectedIndex, _preSelectedIndex);
                        //if (_selectedIndex >= _preSelectedIndex)
                        //{
                        //    //first minus, then process
                        //    _preSelectedIndex -= _scrollBar.LargeChange / _itemHeight + 1;

                        //    if (_preSelectedIndex < 0) _preSelectedIndex = 0;
                        //    _scrollBar.Value -= _scrollBar.LargeChange;
                        //    if (_preSelectedIndex == 0)
                        //    {
                        //        _scrollBar.Value = _scrollBar.Minimum;
                        //    }
                        //    int startIndex = _selectedIndex;
                        //    if (_indicates.Count > 0 && _indicates.Count < _items.Count)
                        //        startIndex = Convert.ToInt32(_indicates[_indicates.Count - 1]) + 1;
                        //    else if (_indicates.Count == _items.Count)
                        //        startIndex = -1;
                        //    processKeyPageDownUpHomeEndMultiSelect(startIndex, _selectedIndex, _preSelectedIndex);

                        //}
                        //else
                        //{
                        //    //first process, then minus
                        //    processKeyUpMultiSelect(_selectedIndex, _preSelectedIndex);
                        //    _preSelectedIndex -= _scrollBar.LargeChange / _itemHeight + 1;

                        //    if (_preSelectedIndex < 0) _preSelectedIndex = 0;
                        //    _scrollBar.Value -= _scrollBar.LargeChange;
                        //    if (_preSelectedIndex == 0)
                        //    {
                        //        _scrollBar.Value = _scrollBar.Minimum;
                        //    }

                        //}
                    }
                    else if(e.KeyCode == Keys.Home)
                    {
                        //first minus, then process
                        _preSelectedIndex = 0;
                        _scrollBar.Value = _scrollBar.Minimum;

                        int startIndex = _selectedIndex;
                        if (_indicates.Count > 0 && _indicates.Count <= _items.Count)
                            startIndex = Convert.ToInt32(_indicates[_indicates.Count - 1]);
                        else if (_indicates.Count == _items.Count)
                            startIndex = -1;
                        processKeyPageDownUpHomeEndMultiSelect(startIndex, _selectedIndex, _preSelectedIndex);

                        //if (_selectedIndex >= _preSelectedIndex)
                        //{
                        //    //first minus, then process
                        //    _preSelectedIndex = 0;
                        //    _scrollBar.Value = _scrollBar.Minimum;
                        //    processKeyUpMultiSelect(_selectedIndex, _preSelectedIndex);

                        //}
                        //else
                        //{
                        //    //first process, then minus
                        //    processKeyUpMultiSelect(_selectedIndex, _preSelectedIndex);
                        //    _preSelectedIndex = 0;
                        //    _scrollBar.Value = _scrollBar.Minimum;

                        //}
                    }

                    Invalidate();
                }


            }
            //single select
            //if ctrl is pressed before
            else if (!e.Control && !e.Shift)
            {
                _ctrlPressed = false;
                _shiftPressed = false;
                _multiKeyDown = false;
                if (e.KeyCode == Keys.Down)
                {
                    _multiKeySelectNeedInit = true;
                    //clear multi select
                    if (_indicates.Count > 0)
                    {
                        _indicates.Clear();
                        _selectedItems.Clear();
                    }
                    //single select processing
                    int lastItem = lastIndexInBox();

                    if (_preSelectedIndex >= 0)
                    {
                        _selectedIndex = _preSelectedIndex;
                        _preSelectedIndex = -1;
                    }
                    _selectedIndex += 1;
                    if (_selectedIndex >= _items.Count)
                    {
                        _selectedIndex = _items.Count - 1;
                    }
                    else if (_selectedIndex > lastItem)
                    {
                        _scrollBar.Value += _scrollBar.SmallChange;
                        if(_selectedIndex == _items.Count - 1)
                        {
                            _scrollBar.Value = _scrollBar.Maximum;
                        }
                    }

                    updateSelection();
                    Invalidate();
                }
                else if (e.KeyCode == Keys.Up)
                {
                    _multiKeySelectNeedInit = true;
                    //if (_selectedIndex < 0) return;
                    //
                    //clear multi select
                    if (_indicates.Count > 0)
                    {
                        _indicates.Clear();
                        _selectedItems.Clear();
                    }
                    //single select processing
                    int firstItem = firstIndexInBox();
                    if (_preSelectedIndex >= 0)
                    {
                        _selectedIndex = _preSelectedIndex;
                        _preSelectedIndex = -1;
                    }

                    _selectedIndex -= 1;
                    if (_selectedIndex < 0)
                    {
                        _selectedIndex = 0;
                    }
                    else if (_selectedIndex < firstItem)
                    {
                        _scrollBar.Value -= _scrollBar.SmallChange;
                        if(_selectedIndex == 0)
                        {
                            _scrollBar.Value = _scrollBar.Minimum;
                        }
                    }
                    updateSelection();
                    Invalidate();
                }
                else if(e.KeyCode == Keys.PageDown)
                {
                    _multiKeySelectNeedInit = true;
                    //clear multi select
                    if (_indicates.Count > 0)
                    {
                        _indicates.Clear();
                        _selectedItems.Clear();
                    }
                    //single select processing
                    if (_preSelectedIndex >= 0)
                    {
                        _selectedIndex = _preSelectedIndex;
                        _preSelectedIndex = -1;
                    }
                    int lastItem = lastIndexInBox();
                    if(_selectedIndex == lastItem)
                    {
                        _scrollBar.Value += _scrollBar.LargeChange;
                        _selectedIndex = lastIndexInBox();
                    }
                    else if(_selectedIndex < lastItem)
                    {
                        _selectedIndex = lastItem;
                    }
                    if (_selectedIndex == _items.Count - 1)
                    {
                        _scrollBar.Value = _scrollBar.Maximum;
                    }
                    updateSelection();
                    Invalidate();
                }
                else if(e.KeyCode == Keys.PageUp)
                {
                    _multiKeySelectNeedInit = true;
                    //if (_selectedIndex < 0) return;
                    //
                    //clear multi select
                    if (_indicates.Count > 0)
                    {
                        _indicates.Clear();
                        _selectedItems.Clear();
                    }
                    //single select processing
                    if (_preSelectedIndex >= 0)
                    {
                        _selectedIndex = _preSelectedIndex;
                        _preSelectedIndex = -1;
                    }
                    int firstItem = firstIndexInBox();
                    if (_selectedIndex == firstItem)
                    {
                        _scrollBar.Value -= _scrollBar.LargeChange;
                        _selectedIndex = firstIndexInBox();
                    }
                    else if(_selectedIndex > firstItem)
                    {
                        _selectedIndex = firstItem;
                    }

                    if (_selectedIndex == 0)
                    {
                        _scrollBar.Value = _scrollBar.Minimum;
                    }
                    updateSelection();
                    Invalidate();
                }
                else if(e.KeyCode == Keys.Home)
                {
                    _multiKeySelectNeedInit = true;
                    //if (_selectedIndex < 0) return;
                    //
                    //clear multi select
                    if (_indicates.Count > 0)
                    {
                        _indicates.Clear();
                        _selectedItems.Clear();
                    }
                    //single select processing
                    if (_preSelectedIndex >= 0)
                    {
                        //_selectedIndex = _preSelectedIndex;
                        _preSelectedIndex = -1;
                    }

                    _selectedIndex = 0;
                    _scrollBar.Value = _scrollBar.Minimum;

                    updateSelection();
                    Invalidate();

                }
                else if(e.KeyCode == Keys.End)
                {
                    _multiKeySelectNeedInit = true;
                    //clear multi select
                    if (_indicates.Count > 0)
                    {
                        _indicates.Clear();
                        _selectedItems.Clear();
                    }
                    //single select processing
                    if (_preSelectedIndex >= 0)
                    {
                        //_selectedIndex = _preSelectedIndex;
                        _preSelectedIndex = -1;
                    }
                    _selectedIndex = _items.Count - 1;
                    _scrollBar.Value = _scrollBar.Maximum;

                    updateSelection();
                    Invalidate();
                }
            }

            base.OnKeyDown(e);
        }

        protected override void OnKeyUp(KeyEventArgs e)
        {

            if (e.KeyCode == Keys.ControlKey) _ctrlPressed = false;
            else if (e.KeyCode == Keys.ShiftKey) _shiftPressed = false;

            base.OnKeyUp(e);
        }

        protected override void OnKeyPress(KeyPressEventArgs e)
        {

            base.OnKeyPress(e);
        }

        protected override bool ProcessDialogKey(Keys keyData)
        {
            //transfer other sub controls' focus to this control
            Focus();
            //process  other controls  key event when it happened
            if (keyData == Keys.Down || keyData == Keys.Up ||
                keyData == Keys.PageDown || keyData == Keys.PageUp ||
                keyData == Keys.Home || keyData == Keys.End)
            {
                KeyEventArgs e = new KeyEventArgs(keyData);
                OnKeyDown(e);
                return true;
            }

            return base.ProcessDialogKey(keyData);
  
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            Cursor = Cursors.Hand;
            updateHoveredItem(e);

            Invalidate();
        }

        

        private void updateHoveredItem(MouseEventArgs e)
        {
            int index = _scrollBar.Value / _itemHeight + e.Location.Y / _itemHeight;

            if (index >= Items.Count)
            {
                index = -1;
            }

            if (index >= 0 && index < Items.Count)
            {
                _hoveredItem = index;
            }

        }

        protected override void OnMouseLeave(EventArgs e)
        {
            _hoveredItem = -1;
            Cursor = Cursors.Default;
            Invalidate();
            base.OnMouseLeave(e);
        }

        protected override void OnHandleCreated(EventArgs e)
        {
            base.OnHandleCreated(e);
            _scrollBar.Size = new Size(12, Height - (ShowBorder ? 2 : 0));
            _scrollBar.Location = new Point(Width - (_scrollBar.Width + (ShowBorder ? 1 : 0)), ShowBorder ? 1 : 0);
            InvalidateScroll(this, e);
        }

        public const int WM_SETCURSOR = 0x0020;
        public const int IDC_HAND = 32649;

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern IntPtr LoadCursor(IntPtr hInstance, int lpCursorName);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern IntPtr SetCursor(IntPtr hCursor);

        protected override void WndProc(ref Message m)
        {
            if (m.Msg == WM_SETCURSOR)
            {
                SetCursor(LoadCursor(IntPtr.Zero, IDC_HAND));
                m.Result = IntPtr.Zero;
                return;
            }
            base.WndProc(ref m);
        }
    }

    #endregion

}
